/**
 * 
 */
package edu.umd.clip.lm.storage.compact;

import java.io.*;
import java.util.*;

import edu.umd.clip.lm.factors.*;
import edu.umd.clip.lm.model.*;
import edu.umd.clip.lm.util.*;

/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public class DbDescriptor implements Externalizable {
	private String filenames[];
	private CompactReadOnlyLong2ObjectHashMap<WordFileDesc> wordDescriptors;
	private long totalClusterCounts[];
	
	// NOTE: zero is not a legitimate index
	// TODO: these can be shared among all databases
	private int compactFactors[];
	private CompactReadOnlyInt2IntHashMap revCompactFactors;
	
	
	public DbDescriptor(int maxClusterId) {
		totalClusterCounts = new long[maxClusterId+1];
		
		Experiment exp = Experiment.getInstance();
		FactorTupleDescription desc = exp.getTupleDescription();
		
		//wordDescriptors = new HashMap<FactorTuple, WordFileDesc>(desc.getAllOvertFactors().size());
		
		compactFactors = new int[exp.getHiddenPrefixes().size()+1];
		int factorIndices[] = new int[compactFactors.length];
		
		int idx = 1;
		compactFactors[0] = -1;
		factorIndices[0] = 0;
		for(FactorTuple hiddenFactors : exp.getHiddenPrefixes().keySet()) {
			int compact = desc.packHiddenFactorsToInt(hiddenFactors.getBits());
			compactFactors[idx] = compact;
			factorIndices[idx] = idx;
			++idx;
		}
		revCompactFactors = new CompactReadOnlyInt2IntHashMap(compactFactors, factorIndices, 1.0f);
	}
	
	public DbDescriptor(ObjectInput in) throws IOException, ClassNotFoundException {
		readExternal(in);
	}
	
	public static class WordFileDesc implements Externalizable {
		public byte fileId;
		public int offset;
		public int size;
		public boolean compressed;
		
		public WordFileDesc(ObjectInput in) throws IOException {
			readExternal(in);
		}
		
		/**
		 * @param fileId
		 * @param offset
		 * @param size
		 * @param compressed
		 */
		public WordFileDesc(byte fileId, int offset, int size,
				boolean compressed) {
			this.fileId = fileId;
			this.offset = offset;
			this.size = size;
			this.compressed = compressed;
		}
		/* (non-Javadoc)
		 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
		 */
		@Override
		public void writeExternal(ObjectOutput out) throws IOException {
			out.writeByte(fileId);
			out.writeInt(offset);
			out.writeInt(size);
			out.writeBoolean(compressed);
		}
		/* (non-Javadoc)
		 * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
		 */
		@Override
		public void readExternal(ObjectInput in) throws IOException {
			fileId = in.readByte();
			offset = in.readInt();
			size = in.readInt();
			compressed = in.readBoolean();
		}
	}
	
	/* (non-Javadoc)
	 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
	 */
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(filenames);
		out.writeObject(compactFactors);
		out.writeObject(totalClusterCounts);
		
		out.writeInt(wordDescriptors.size());
		long words[] = wordDescriptors.keys();
		WordFileDesc descs[] = wordDescriptors.values(new WordFileDesc[wordDescriptors.size()]);
		
		for(int i=0; i<words.length; ++i) {
			out.writeLong(words[i]);
			descs[i].writeExternal(out);
		}
	}

	/* (non-Javadoc)
	 * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
	 */
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		filenames = (String[]) in.readObject();
		compactFactors = (int[]) in.readObject();
		totalClusterCounts = (long[]) in.readObject();
		
		int size = in.readInt();
		long words[] = new long[size];
		WordFileDesc descs[] = new WordFileDesc[size];
		for(int i=0; i<size; ++i) {
			words[i] = in.readLong();
			descs[i] = new WordFileDesc(in);
		}
		wordDescriptors = new CompactReadOnlyLong2ObjectHashMap<WordFileDesc>(words, descs, 1.0f);
		
		int factorIndices[] = new int[compactFactors.length];
		
		factorIndices[0] = 0;
		for(int idx=1; idx < compactFactors.length; ++idx) {
			factorIndices[idx] = idx;
			++idx;
		}
		revCompactFactors = new CompactReadOnlyInt2IntHashMap(compactFactors, factorIndices, 1.0f);
	}

	
	public WordFileDesc getWordFileDesc(long factorBits) {
		return wordDescriptors.get(factorBits);
	}
/*
	public void setWordFileDesc(long factorBits, WordFileDesc desc) {
		wordDescriptors.put(new FactorTuple(factorBits), desc);
	}
	
	public void setWordFileDesc(FactorTuple word, WordFileDesc desc) {
		wordDescriptors.put(word, desc);
	}
*/	
	public String getFilename(byte fileid) {
		return filenames[fileid];
	}
	
	public long getClusterCount(int clusterid) {
		return totalClusterCounts[clusterid];
	}
	
	public void setClusterCount(int clusterid, long count) {
		totalClusterCounts[clusterid] = count;
	}
	
	public int getCompactFactorsIndex(int compactFactors) {
		int idx = revCompactFactors.get(compactFactors);
		if (idx == 0) {
			throw new Error("not existing compactFactors: " + compactFactors);
		}
		return idx;
	}
	
	public int getCompactFactors(int index) {
		return compactFactors[index];
	}

	public void setFilenames(String[] filenames) {
		this.filenames = filenames;
	}

	public String[] getFilenames() {
		return filenames;
	}

	public void setWordDescriptors(
			CompactReadOnlyLong2ObjectHashMap<WordFileDesc> wordDescriptors) {
		this.wordDescriptors = wordDescriptors;
	}
}
